
/**
 * @link https://www.sequelize.cn/core-concepts/model-querying-basics
 */
const Op = {
    /**
     * @description 
     * @example
     * ```javascript
     *  [{ a: 5 }, { b: 6 }] => (a = 5) AND (b = 6)
     * ```
     */
    and: Symbol('and'),
    or: Symbol('or'),
    eq: Symbol('eq'),
    neq: Symbol('neq'),
    not: Symbol('not'),
    gt: Symbol('gt'),
    gte: Symbol('gte'),
    lt: Symbol('lt'),
    lte: Symbol('lte'),
    between: Symbol('between'),
    notBetween: Symbol('notBetween'),
    in: Symbol('in'),
    notIn: Symbol('notIn'),
    like: Symbol('like'),
    notLike: Symbol('notLike'),
    startsWith: Symbol('startsWith'),
    endsWith: Symbol('endsWith'),
    substring: Symbol('substring'),
    iLike: Symbol('iLike'),
    notILike: Symbol('notILike'),
    regexp: Symbol('regexp'),
    notRegexp: Symbol('notRegexp'),
    iRegexp: Symbol('iRegexp'),
    notIRegexp: Symbol('notIRegexp'),
    any: Symbol('any'),
    match: Symbol('match')
}
export class StrongArray<T extends {[key: string]: any}>{
    protected _length = 0;
    protected originKeys: string[] = [];
    protected sortKeys: string[] = []
    protected valueMap: Map<string, T> = new Map();
    protected _sortBy = '';
    protected _sortType:'ascend' | 'descend' | '' = '';
    public get length () {
        return this.originKeys.length;
    }
    constructor(arr: T[], protected uniqKey: string) {
        this._length = arr.length;
        for(let i = 0; i < arr.length; i++) {
            // @ts-ignore
            this.originKeys.push(arr[i][uniq]);
            // @ts-ignore
            this.valueMap.set(arr[i][uniq], arr[i]);
        }
    }

    public push(...values: T[]) {
        for(let value of values) {
            let key = value[this.uniqKey];
            this.valueMap.set(key, value);
        }
        this.refreshKeys();
    }
    public keys() {
        return this.sortKeys.length === 0 ? this.originKeys : this.sortKeys;
    }
    public findIndex(callback: (val: T) => boolean) {
        const keys = this.sortKeys.length === 0 ? this.originKeys : this.sortKeys;
        for(let i = 0; i < keys.length; i++) {
            let val = this.valueMap.get(keys[i]);
            if(callback(val)) {
                return i;
            }
        }
        return -1;
    }
    public find(callback: (val: T) => boolean) {
        const idx =this.findIndex(callback);
        if(idx === -1) {
            return undefined;
        }
        const keys = this.sortKeys.length === 0 ? this.originKeys : this.sortKeys;
        return this.valueMap.get(keys[idx])
    }
    protected refreshKeys() {
        const originKeysSet = new Set(this.valueMap.keys());
        this.originKeys = this.originKeys.filter(item => originKeysSet.has(item));
        this.sortKeys = this.sortKeys.filter(item => originKeysSet.has(item));
    }
    public updateOrInsetByUniq(uniq: any, value: any) {
        if(value[this.uniqKey] !== uniq) {
            console.warn(`update uniq of value not equal received uniq, value's uniq is ${value[this.uniqKey]}, but received ${uniq}!`)
            return;
        }
        const has = this.valueMap.has(uniq);
        this.valueMap.set(uniq, value);
        if(!has) {
            this.originKeys.unshift(uniq);
            this.refreshKeys();
            this._sort();
        }
    }
    public deleteByUniq(uniq: any) {
        this.valueMap.delete(uniq);
        this.refreshKeys();
    }
    public filter(callback: ((val: any) => boolean) | null | undefined) {
        const keys = this.sortKeys.length === 0 ? this.originKeys : this.sortKeys;
        const values = keys.map(item => this.valueMap.get(item)).filter(callback);
        return values;
    }
    public deleteByKeyValue(key: string, value: any) {
        for(let item of this.originKeys) {
            let val = this.valueMap.get(item);
            if(val[key] === value) {
                this.valueMap.delete(item);
            }
        }
        this.refreshKeys();
    }
    public getValueByUniq(uniq: any) {
        return this.valueMap.get(uniq);
    }
    protected getSortValue(uniq: string) {
        try {
            let obj = this.valueMap.get(uniq) as T;
            let num = Number(obj[this._sortBy]);
            return isNaN(num) ? -Infinity:num;
        } catch (error) {
            return -Infinity;
        }
    }
    protected _sort() {
        let sortKey: string[] = [...this.originKeys];
        if(this._sortType === '') {
            // sortKey = this.originKeys;
        } else {
            for(let i = 0; i < sortKey.length; i++) {
                for(let j = 0; j < sortKey.length; j++) {
                    let pre = this.getSortValue(sortKey[i]);
                    let current = this.getSortValue(sortKey[j]);
                    if(pre > current) {
                        let temp = sortKey[i];
                        sortKey[i] = sortKey[j];
                        sortKey[j] = temp;
                    }
                }
            }
            if(this._sortType === 'ascend') {
                sortKey = sortKey.reverse();
            }
        }
        this.sortKeys = sortKey;
    }
    /**
     * @description 更新排序规则
     * @param sortBy 
     * @param sortType 
     */
    public setSort(sortBy: string, sortType: 'ascend' | 'descend' | '' | undefined | null) {
        this.setSortBy(sortBy, false).setSortType(sortType);
    }
    /**
     * @description 依据排序字段名
     * @param key
     * @returns 
     */
    public setSortBy(key: string, autoSort = true) {
        this._sortBy = key;
        if(autoSort) {
            this._sort();
        }
        return this;
    }
    /**
     * 
     * @param type 排序方式
     * @returns 
     */
    public setSortType(type: 'ascend' | 'descend' | '' | undefined | null, autoSort = true) {
        if(!type) {
            this._sortType = '';
        } else {
            this._sortType = type;
        }
        if(autoSort) {
            this._sort();
        }
        return this;
    }
    [Symbol.iterator](){
        let i = 0;
        let len = this._length;
        const keys = this.sortKeys.length === 0 ? this.originKeys : this.sortKeys;
        return {
            next: () => {
                return { value: this.valueMap.get(keys[i]), done: i++ >= len };
            }
        }
    }
}